﻿using System;
using System.Collections.Generic;
using System.Linq;
using NHibernate.Mapping.ByCode;
using Xenta.Enums;
using Xenta.Utils;

namespace Xenta.Entities
{
    /// <summary>
    /// Represents the forum entity.
    /// </summary>
    /// <remarks><pre>
    /// The entity regarding the disclosure level.
    /// 
    /// + Entire
    ///  | - AccessRules(of the Extended level)
    ///  + Extended
    ///   + Base
    ///    | - Parent(of the Primary level)
    ///    | - LastTopic(of the Base level)
    ///    | - DisplayOrder
    ///    | - TopicCount
    ///    | - PostCount
    ///    | - Flags
    ///    | - UpdatedOn
    ///    | - CreatedOn
    ///    | - Translations(of the same level)
    ///    + Keys
    ///     + Primary
    ///      | - EntityID
    /// 
    /// Note: upper level includes lower level.
    /// </pre></remarks>
    public class ForumEntity : AuditableEntity
    {
        #region Fields

        private ForumEntity _parent;
        private ForumTopicEntity _lastTopic;
        private ICollection<ForumEntity> _childs;
        private ICollection<ForumTopicEntity> _topics;
        private ICollection<TForumEntity> _translations;
        private ICollection<ForumAccessRuleEntity> _accessRules;

        #endregion

        #region Ctors

        /// <summary>
        /// Initializes a new class instance.
        /// </summary>
        public ForumEntity()
        {
            _parent = null;
            _lastTopic = null;
            _childs = new List<ForumEntity>();
            _topics = new List<ForumTopicEntity>();
            _translations = new List<TForumEntity>();
            _accessRules = new List<ForumAccessRuleEntity>();
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the parent forum identifier.
        /// </summary>
        public virtual int ParentID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the parent forum.
        /// </summary>
        public virtual ForumEntity Parent
        {
            get
            {
                return ParentID != 0 ? _parent : null;
            }
            set
            {
                _parent = value;
                ParentID = value != null ? value.EntityID : 0;
            }
        }

        /// <summary>
        /// Gets or sets the last topic identifier.
        /// </summary>
        public virtual int LastTopicID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the last topic.
        /// </summary>
        public virtual ForumTopicEntity LastTopic
        {
            get
            {
                return LastTopicID != 0 ? _lastTopic : null;
            }
            set
            {
                _lastTopic = value;
                LastTopicID = value != null ? value.EntityID : 0;
            }
        }

        /// <summary>
        /// Gets or sets the topic count.
        /// </summary>
        public virtual int TopicCount
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the post count.
        /// </summary>
        public virtual int PostCount
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the display order.
        /// </summary>
        public virtual int DisplayOrder
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flags.
        /// </summary>
        public virtual ForumFlags Flags
        {
            get;
            set;
        }

        #endregion

        #region Collections

        /// <summary>
        /// Gets or sets the child forum collection.
        /// </summary>
        public virtual ICollection<ForumEntity> Childs
        {
            get
            {
                return _childs;
            }
            set
            {
                _childs = value;
            }
        }

        /// <summary>
        /// Gets or sets the topic collection.
        /// </summary>
        public virtual ICollection<ForumTopicEntity> Topics
        {
            get
            {
                return _topics;
            }
            set
            {
                _topics = value;
            }
        }

        /// <summary>
        /// Gets or sets the forum translations.
        /// </summary>
        public virtual ICollection<TForumEntity> Translations
        {
            get
            {
                return _translations;
            }
            set
            {
                _translations = value;
            }
        }

        /// <summary>
        /// Gets or sets the forum access rules.
        /// </summary>
        public virtual ICollection<ForumAccessRuleEntity> AccessRules
        {
            get
            {
                return _accessRules;
            }
            set
            {
                _accessRules = value;
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Gets the forum translation.
        /// </summary>
        /// <param name="languageID">The language identifier.</param>
        /// <returns>The forum translation.</returns>
        public virtual TForumEntity Translation(int languageID)
        {
            return _translations.SingleOrDefault(x => x.Language.EntityID == languageID);
        }

        #endregion

        #region Utilities

        protected override void Disclose(IDictionary<String, Object> obj,
            DisclosureLevel level,
            IDictionary<String, String> options)
        {
            base.Disclose(obj, level, options);
            if(level >= DisclosureLevel.Base)
            {
                obj["Parent"] = Parent != null
                    ? Parent.Disclose(DisclosureLevel.Primary, options)
                    : null;
                obj["LastTopic"] = LastTopic != null
                    ? LastTopic.Disclose(DisclosureLevel.Base, options)
                    : null;
                obj["DisplayOrder"] = DisplayOrder;
                obj["TopicCount"] = TopicCount;
                obj["PostCount"] = PostCount;
                obj["Flags"] = Flags.ToString();
                obj["Translations"] = (options.ContainsKey("TranslatedTo")
                    ? Translations.Where(x => options["TranslatedTo"].Contains(x.Language.Code))
                    : Translations)
                    .Map(x => x.Disclose(level, options));
            }
            if(level >= DisclosureLevel.Extended)
                obj["AccessRules"] = AccessRules.Map(x => x.Disclose(DisclosureLevel.Extended, options));
        }

        #endregion
    }

    /// <summary>
    /// The entity NHibernate mapping.
    /// </summary>
    public class ForumMapping : EntityMapping<ForumEntity>
    {
        /// <summary>
        /// Initializes a new class instance.
        /// </summary>
        public ForumMapping()
        {
            Table("fwk_Forums");
            Id(x => x.EntityID, m =>
            {
                m.Column("ForumID");
                m.Generator(Generators.Identity);
            });
            Property(x => x.ParentID, m =>
            {
                m.Column("ParentForumID");
                m.NotNullable(true);
            });
            ManyToOne(x => x.Parent, m =>
            {
                m.Column("ParentForumID");
                m.NotFound(NotFoundMode.Ignore);
                m.Insert(false);
                m.Update(false);
            });
            Property(x => x.LastTopicID, m =>
            {
                m.Column("LastTopicID");
                m.NotNullable(false);
            });
            ManyToOne(x => x.LastTopic, m =>
            {
                m.Column("LastTopicID");
                m.NotFound(NotFoundMode.Ignore);
                m.Insert(false);
                m.Update(false);
            });
            Property(x => x.TopicCount, m =>
            {
                m.NotNullable(true);
            });
            Property(x => x.PostCount, m =>
            {
                m.NotNullable(true);
            });
            Property(x => x.DisplayOrder, m =>
            {
                m.NotNullable(true);
            });
            Property(x => x.Flags, m =>
            {
                m.NotNullable(true);
            });
            Property(x => x.CreatedOn, m =>
            {
                m.NotNullable(true);
            });
            Property(x => x.UpdatedOn, m =>
            {
                m.NotNullable(true);
            });
            Bag(x => x.Childs, m =>
            {
                m.Table("fwk_Forums");
                m.Key(k =>
                {
                    k.Column("ParentForumID");
                    k.NotNullable(true);
                });
                m.Inverse(true);
            },
            a =>
            {
                a.OneToMany();
            });
            Bag(x => x.Topics, m =>
            {
                m.Table("fwk_ForumTopics");
                m.Key(k =>
                {
                    k.Column("ForumID");
                    k.ForeignKey("FK_fwk_ForumTopics_fwk_Forums");
                    k.NotNullable(true);
                });
                m.Inverse(true);
            },
            a =>
            {
                a.OneToMany();
            });
            Bag(x => x.Translations, m =>
            {
                m.Table("fwk_TForums");
                m.Key(k =>
                {
                    k.Column("ForumID");
                    k.ForeignKey("FK_fwk_TForums_fwk_Forums");
                    k.NotNullable(true);
                });
                m.Inverse(true);
                m.BatchSize(10);
            },
            a =>
            {
                a.OneToMany();
            });
            Bag(x => x.AccessRules, m =>
            {
                m.Table("fwk_ForumAccessRules");
                m.Key(k =>
                {
                    k.Column("ForumID");
                    k.ForeignKey("FK_fwk_ForumAccessRules_fwk_Forums");
                    k.NotNullable(true);
                });
                m.Inverse(true);
                m.BatchSize(10);
            },
            a =>
            {
                a.OneToMany();
            });
            Cache(m =>
            {
                m.Usage(CacheUsage.ReadWrite);
            });
        }
    }
}
